import { BaseRecycle } from '../Basic/Manager/PoolManager';

export namespace Task {
  export type next = (data?: any) => boolean | symbol | void;
  export type retry = (timeout?: number) => boolean | 'ASYNC';
  export type end = (data: any, success: boolean) => boolean;

  interface IHandle {
    (next: next, retry: retry, end: end): void;
  }

  interface IFinish<T> {
    (results?: T, success?: boolean, data?: any): any;
  }

  interface ITask<T> {
    isRunning: boolean;
    /** 任务个数 */
    size(): number;
    /** 任务添加 */
    add(handle: IHandle): this;
    /** 在最前插入任务 */
    first(handle: IHandle): this;
    /** 开始执行 */
    start(finish?: IFinish<T> | Function): this;
    /** 停止任务 */
    end: (index?: number, data?: any, success?: boolean) => boolean;
  }

  class TaskItem extends BaseRecycle {
    className: string = 'Task.TaskItem';
    private handle: IHandle = null;

    reuse(handle: IHandle) {
      this.handle = handle;
    }

    unuse() {
      this.handle = null;
    }

    execute(next: next, retry: retry, end: end) {
      this.handle(next, retry, end);
      this.release();
    }
  }

  /**
   * 顺序执行
   */
  export class Sync<T extends Array<any>> extends BaseRecycle implements ITask<T> {
    className: string = 'Task.Sync';
    private running = false;
    private index: number = -1;
    private list: TaskItem[] = [];
    private finish: IFinish<T> | Function = null;

    // 每个item的返回值，通过next或end存储
    results: T = [] as T;

    reuse() {}
    unuse() {
      this.running = false;
      this.index = -1;
      this.list = [];
      this.finish = null;
      this.results = [] as T;
    }

    size(): number {
      return this.list.length;
    }

    add(handle: IHandle) {
      this.list.push(TaskItem.pop(handle));
      this.results.push(undefined);
      return this;
    }

    first(handle: IHandle) {
      this.list.splice(0, 0, TaskItem.pop(handle));
      this.results.splice(0, 0, undefined);
      return this;
    }

    start(finish?: IFinish<T> | Function) {
      if (this.running) {
        return this;
      }

      this.running = true;
      this.index = -1;
      this.finish = finish;

      this.next(this.index);

      return this;
    }

    end(index?: number, data?: any, success?: boolean): boolean {
      if (!this.running) {
        return false;
      }

      // if (typeof data !== 'undefined') {
      //     this.results[this.index] = data;
      // }

      this.running = false;
      if (this.finish) {
        this.finish(this.results, success ?? true, data);
      }
      this.release();
      return true;
    }

    private next(index: number, data?: any): boolean | symbol {
      if (!this.running) {
        return false;
      }

      if (index !== this.index) return false;

      if (typeof data !== 'undefined') {
        this.results[this.index] = data;
      }

      if (++this.index < this.list.length) {
        this.retry(this.index);
      } else {
        this.end();
      }

      return true;
    }

    private retry(index: number): boolean {
      if (!this.running) {
        return false;
      }

      if (index !== this.index) return false;

      const taskItem = this.list[index];
      taskItem &&
        taskItem.execute(
          (data?: any) => this.next(index, data),
          (timeout = 0) => (Number(timeout) > 0 ? (setTimeout(() => this.retry(index), Number(timeout) * 1000), 'ASYNC') : this.retry(index)),
          (data?: any, success?: boolean) => this.end(index, data, success)
        );

      return true;
    }

    get isRunning(): boolean {
      return this.running;
    }
  }

  /**
   * 同时执行
   */
  export class ASync<T extends Array<any>> extends BaseRecycle implements ITask<T> {
    className: string = 'Task.Async';
    private running = false;
    private count: number = 0;
    private list: TaskItem[] = [];
    private finish: IFinish<T> | Function = null;

    // 每个item的返回值，通过next或end存储
    results: T = [] as T;

    reuse() {}
    unuse() {
      this.running = false;
      this.count = 0;
      this.list = [];
      this.finish = null;
      this.results = [] as T;
    }

    /** 插入同步任务，如果任务链还没有结束则会同步一起执行，如果结束则会返回空 */
    first(handle: IHandle): this {
      return this.add(handle);
    }

    size(): number {
      return this.list.length;
    }

    add(handle: IHandle) {
      this.list.push(TaskItem.pop(handle));
      this.results.push(undefined);

      if (this.running) {
        this.retry(this.list.length - 1);
      }
      return this;
    }

    start(finish?: IFinish<T> | Function) {
      if (this.running) {
        return this;
      }

      this.running = true;
      this.count = 0;
      this.finish = finish;

      if (this.list.length) {
        for (let index = 0; index < this.list.length; index++) {
          this.retry(index);
        }
      } else {
        this.end && this.end(this.count);
      }

      return this;
    }

    end(index: number, data?: any, success?: boolean): boolean {
      if (!this.running) {
        return false;
      }

      if (index >= 0 && index < this.results.length) {
        if (this.results[index] || this.results[index] === null) return false;
        this.results[index] = typeof data !== 'undefined' ? data : null;
      }

      this.running = false;
      if (this.finish) {
        this.finish(this.results, success ?? true, data);
      }
      this.release();
      return true;
    }

    private next(index: number, data?: any): boolean {
      if (!this.running) {
        return false;
      }

      if (index >= 0 && index < this.results.length) {
        if (this.results[index] || this.results[index] === null) return false;
        this.results[index] = typeof data !== 'undefined' ? data : null;
      }

      if (++this.count === this.list.length) {
        this.end && this.end(this.count);
      }

      return true;
    }

    private retry(index: number): boolean {
      if (!this.running) {
        return false;
      }

      const taskItem = this.list[index];
      taskItem &&
        taskItem.execute(
          (data?: any) => this.next(index, data),
          (timeout = 0) => (Number(timeout) > 0 ? (setTimeout(() => this.retry(index), Number(timeout) * 1000), 'ASYNC') : this.retry(index)),
          (data: any, success: boolean) => this.end(index, data, success)
        );

      return true;
    }

    get isRunning(): boolean {
      return this.running;
    }
  }

  /**
   * 混合执行
   */
  export class AnySync<T extends Array<any>> extends BaseRecycle implements ITask<T> {
    className: string = 'Task.Any';
    private task = new Sync();

    reuse() {
      this.task = Sync.pop();
    }
    unuse() {
      this.task.unuse();
    }

    // 每个item的返回值，通过next或end存储
    get results(): T {
      return this.task.results as T;
    }

    size() {
      return this.task.size();
    }

    first(handles: IHandle | IHandle[]): this {
      if (handles instanceof Array) {
        const async = ASync.pop();
        handles.forEach((handle) => async.add(handle));
        this.task.add(async.start.bind(async));
      } else {
        this.task.first(handles);
      }
      return this;
    }

    add(handles: IHandle | IHandle[]) {
      if (handles instanceof Array) {
        const async = ASync.pop();
        handles.forEach((handle) => async.add(handle));
        this.task.add(async.start.bind(async));
      } else {
        this.task.add(handles);
      }
      return this;
    }

    start(finish?: IFinish<T> | Function) {
      this.task.start(finish);
      return this;
    }

    end(index?: number, data?: any, success?: boolean): boolean {
      return this.task.end(index, data, success);
    }

    // stop() {
    //     return this.task.stop();
    // }

    // get isStop() {
    //     return this.task.isStop;
    // }

    get isRunning(): boolean {
      return this.task.isRunning;
    }
  }

  interface IExecuteCallBack {
    (retry: (timeout?: number) => void): void;
  }

  /**
   * 任务顺序执行
   */
  export function createSync<T extends Array<any>>(): Sync<T> {
    return Sync.pop<Sync<T>>();
  }

  /**
   * 任务同时执行
   */
  export function createASync<T extends Array<any>>(): ASync<T> {
    return ASync.pop<ASync<T>>();
  }

  /**
   * 根据参数指定执行顺序
   * @example
   * createAny()
   * .add(1).add(2).add(3).add(4)
   * .add([5,6,7])
   * .add(8)
   * 执行顺序，1，2，3，4依次执行，然后同时执行5，6，7，最后执行8
   */
  export function createAny<T extends Array<any>>() {
    return AnySync.pop<AnySync<T>>();
  }

  export function execute(fun: IExecuteCallBack, retryMax = -1, retryFinish?: Function) {
    fun(function retry(timeout = 0) {
      if (retryMax === 0) return retryFinish && retryFinish();
      retryMax = retryMax > 0 ? retryMax - 1 : retryMax;
      if (timeout > 0) {
        setTimeout(() => execute(fun, retryMax, retryFinish), timeout * 1000);
      } else {
        execute(fun, retryMax, retryFinish);
      }
    });
  }

  // export const task = {
  //     /**
  //      * 任务顺序执行
  //      */
  //     createSync<T extends Array<any>>(): Sync<T> {
  //         return new Sync<T>();
  //     },

  //     /**
  //      * 任务同时执行
  //      */
  //     createASync<T extends Array<any>>(): ASync<T> {
  //         return new ASync<T>();
  //     },

  //     /**
  //      * 根据参数指定执行顺序
  //      * @example
  //      * createAny()
  //      * .add(1).add(2).add(3).add(4)
  //      * .add([5,6,7])
  //      * .add(8)
  //      * 执行顺序，1，2，3，4依次执行，然后同时执行5，6，7，最后执行8
  //      */
  //     createAny<T extends Array<any>>() {
  //         return new Any<T>();
  //     },

  //     /**
  //      * 执行单个任务
  //      */
  //     execute(fun: IExecuteCallBack, retryMax = -1, retryFinish?: Function) {
  //         fun(function retry(timeout = 0) {
  //             if (retryMax === 0) return retryFinish && retryFinish();
  //             retryMax = retryMax > 0 ? retryMax - 1 : retryMax;
  //             if (timeout > 0) {
  //                 setTimeout(() => task.execute(fun, retryMax, retryFinish), timeout * 1000);
  //             } else {
  //                 task.execute(fun, retryMax, retryFinish);
  //             }
  //         });
  //     },

  //     /**
  //      * 执行单个任务
  //      * @deprecated
  //      */
  //     excute(fun: IExecuteCallBack, retryMax = -1, retryFinish?: Function) {
  //         return this.execute(fun, retryMax, retryFinish);
  //     },
  // };
}
